// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Matrix.ServiceCommunication.Options;

namespace Matrix.ServiceCommunication.Authentication.Jwt;

/// <summary>
/// JWT Bearer认证提供者
/// </summary>
public class JwtAuthenticationProvider : IAuthenticationProvider
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IOptionsMonitor<ServiceCommunicationOptions> _options;
    private readonly ILogger<JwtAuthenticationProvider> _logger;
    private readonly IMemoryCache _cache;
    private readonly JwtSecurityTokenHandler _tokenHandler;
    private readonly ConcurrentDictionary<string, TokenValidationParameters> _validationParameters;
    private bool _disposed;

    public JwtAuthenticationProvider(
        IHttpClientFactory httpClientFactory,
        IOptionsMonitor<ServiceCommunicationOptions> options,
        ILogger<JwtAuthenticationProvider> logger,
        IMemoryCache cache)
    {
        _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
        _options = options ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        _tokenHandler = new JwtSecurityTokenHandler();
        _validationParameters = new ConcurrentDictionary<string, TokenValidationParameters>();

        // 订阅配置变更
        _options.OnChange(OnConfigurationChanged);

        _logger.LogInformation("JWT Bearer认证提供者已初始化");
    }

    public async Task<string> GetAccessTokenAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var options = _options.CurrentValue;
        var authOptions = options.Authentication;

        if (authOptions.Type != AuthenticationType.JwtBearer)
        {
            _logger.LogDebug("认证类型不是JWT Bearer，返回空字符串");
            return string.Empty;
        }

        var cacheKey = $"jwt_token:{serviceName}";
        if (_cache.TryGetValue(cacheKey, out CachedToken? cachedToken) &&
            cachedToken != null &&
            cachedToken.ExpiresAt > DateTime.UtcNow.AddMinutes(5))
        {
            _logger.LogDebug("使用缓存的JWT Token，服务: {ServiceName}", serviceName);
            return cachedToken.Token;
        }

        try
        {
            var newToken = await RequestNewTokenAsync(serviceName, cancellationToken);
            if (!string.IsNullOrEmpty(newToken.Token))
            {
                // 缓存Token
                var cacheEntry = new CachedToken
                {
                    Token = newToken.Token,
                    RefreshToken = newToken.RefreshToken,
                    ExpiresAt = DateTime.UtcNow.AddSeconds(newToken.ExpiresIn),
                    ServiceName = serviceName
                };

                var cacheOptions = new MemoryCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(Math.Min(newToken.ExpiresIn, 3600))
                };
                _cache.Set(cacheKey, cacheEntry, cacheOptions);

                _logger.LogInformation("获取JWT Token成功，服务: {ServiceName}, 有效期: {ExpiresIn}s",
                    serviceName, newToken.ExpiresIn);
            }

            return newToken.Token ?? string.Empty;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取JWT Token失败，服务: {ServiceName}", serviceName);
            return string.Empty;
        }
    }

    public async Task<string> RefreshTokenAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        // 检查是否有可用的刷新Token
        var cacheKey = $"jwt_token:{serviceName}";
        if (_cache.TryGetValue(cacheKey, out CachedToken? cachedToken) &&
            cachedToken?.RefreshToken != null)
        {
            try
            {
                var refreshedToken = await RefreshTokenWithRefreshTokenAsync(
                    cachedToken.RefreshToken, serviceName, cancellationToken);

                if (!string.IsNullOrEmpty(refreshedToken.Token))
                {
                    // 更新缓存
                    var cacheEntry = new CachedToken
                    {
                        Token = refreshedToken.Token,
                        RefreshToken = refreshedToken.RefreshToken,
                        ExpiresAt = DateTime.UtcNow.AddSeconds(refreshedToken.ExpiresIn),
                        ServiceName = serviceName
                    };

                    var cacheOptions = new MemoryCacheEntryOptions
                    {
                        AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(Math.Min(refreshedToken.ExpiresIn, 3600))
                    };
                    _cache.Set(cacheKey, cacheEntry, cacheOptions);

                    _logger.LogInformation("刷新JWT Token成功，服务: {ServiceName}", serviceName);
                    return refreshedToken.Token;
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "使用刷新Token失败，服务: {ServiceName}", serviceName);
            }
        }

        // 刷新Token失败，清除缓存并重新获取
        _cache.Remove(cacheKey);
        _logger.LogDebug("刷新Token失败，重新获取Token，服务: {ServiceName}", serviceName);

        return await GetAccessTokenAsync(serviceName, cancellationToken);
    }

    public Task<bool> ValidateTokenAsync(string token, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            return Task.FromResult(false);
        }

        try
        {
            var options = _options.CurrentValue.Authentication;
            var validationParameters = GetValidationParameters(options);

            _tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);

            if (validatedToken is JwtSecurityToken jwtToken)
            {
                // 检查Token是否过期
                if (jwtToken.ValidTo < DateTime.UtcNow)
                {
                    _logger.LogWarning("JWT Token已过期，过期时间: {ValidTo}", jwtToken.ValidTo);
                    return Task.FromResult(false);
                }

                // 检查发行者
                if (!string.IsNullOrEmpty(options.Issuer) && jwtToken.Issuer != options.Issuer)
                {
                    _logger.LogWarning("JWT Token发行者不匹配，期望: {ExpectedIssuer}, 实际: {ActualIssuer}",
                        options.Issuer, jwtToken.Issuer);
                    return Task.FromResult(false);
                }

                // 检查受众
                if (!string.IsNullOrEmpty(options.Audience) && !jwtToken.Audiences.Contains(options.Audience))
                {
                    _logger.LogWarning("JWT Token受众不匹配，期望: {ExpectedAudience}, 实际: {ActualAudiences}",
                        options.Audience, string.Join(", ", jwtToken.Audiences));
                    return Task.FromResult(false);
                }

                _logger.LogDebug("JWT Token验证成功，有效期至: {ValidTo}", jwtToken.ValidTo);
                return Task.FromResult(true);
            }

            return Task.FromResult(false);
        }
        catch (SecurityTokenValidationException ex)
        {
            _logger.LogWarning(ex, "JWT Token验证失败");
            return Task.FromResult(false);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "JWT Token验证异常");
            return Task.FromResult(false);
        }
    }

    private async Task<TokenResponse> RequestNewTokenAsync(string serviceName, CancellationToken cancellationToken)
    {
        var authOptions = _options.CurrentValue.Authentication;

        using var client = _httpClientFactory.CreateClient("Authentication");

        var requestData = new Dictionary<string, string>
        {
            { "grant_type", "client_credentials" },
            { "client_id", authOptions.ClientId },
            { "client_secret", authOptions.ClientSecret },
            { "scope", string.Join(" ", authOptions.Scopes ?? new[] { "api.read", "api.write" }) }
        };

        var content = new FormUrlEncodedContent(requestData);
        var response = await client.PostAsync(authOptions.TokenEndpoint, content, cancellationToken);

        if (!response.IsSuccessStatusCode)
        {
            var errorContent = await response.Content.ReadAsStringAsync(cancellationToken);
            _logger.LogError("获取JWT Token失败: {StatusCode} - {Error}",
                response.StatusCode, errorContent);
            return new TokenResponse();
        }

        var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
        var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(responseContent, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return tokenResponse ?? new TokenResponse();
    }

    private async Task<TokenResponse> RefreshTokenWithRefreshTokenAsync(
        string refreshToken, string serviceName, CancellationToken cancellationToken)
    {
        var authOptions = _options.CurrentValue.Authentication;

        using var client = _httpClientFactory.CreateClient("Authentication");

        var requestData = new Dictionary<string, string>
        {
            { "grant_type", "refresh_token" },
            { "refresh_token", refreshToken },
            { "client_id", authOptions.ClientId },
            { "client_secret", authOptions.ClientSecret }
        };

        var content = new FormUrlEncodedContent(requestData);
        var response = await client.PostAsync(authOptions.TokenEndpoint, content, cancellationToken);

        if (!response.IsSuccessStatusCode)
        {
            var errorContent = await response.Content.ReadAsStringAsync(cancellationToken);
            _logger.LogWarning("刷新JWT Token失败: {StatusCode} - {Error}",
                response.StatusCode, errorContent);
            return new TokenResponse();
        }

        var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
        var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(responseContent, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return tokenResponse ?? new TokenResponse();
    }

    private TokenValidationParameters GetValidationParameters(AuthenticationOptions options)
    {
        var key = GetValidationParameters(options);
        if (key != null)
        {
            return key;
        }

        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuer = !string.IsNullOrEmpty(options.Issuer),
            ValidIssuer = options.Issuer,
            ValidateAudience = !string.IsNullOrEmpty(options.Audience),
            ValidAudience = options.Audience,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(options.ClockSkewSeconds > 0 ? options.ClockSkewSeconds : 300),
            IssuerSigningKey = !string.IsNullOrEmpty(options.SecretKey)
                ? new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SecretKey))
                : null
        };

        _validationParameters.TryAdd("default", validationParameters);
        return validationParameters;
    }

    private void OnConfigurationChanged(ServiceCommunicationOptions newOptions)
    {
        // 清除验证参数缓存
        _validationParameters.Clear();

        // 清除Token缓存
        foreach (var key in _cache.Keys)
        {
            if (key.StartsWith("jwt_token:"))
            {
                _cache.Remove(key);
            }
        }

        _logger.LogInformation("JWT配置已变更，已清理缓存");
    }

    public void Dispose()
    {
        if (_disposed) return;

        _disposed = true;
        _validationParameters.Clear();
        _cache.Compact(1.0);

        _logger.LogDebug("JWT Bearer认证提供者已释放");
    }

    /// <summary>
    /// 缓存的Token信息
    /// </summary>
    private class CachedToken
    {
        public string Token { get; set; } = string.Empty;
        public string RefreshToken { get; set; } = string.Empty;
        public DateTime ExpiresAt { get; set; }
        public string ServiceName { get; set; } = string.Empty;
    }

    /// <summary>
    /// Token响应模型
    /// </summary>
    private class TokenResponse
    {
        public string AccessToken { get; set; } = string.Empty;
        public string TokenType { get; set; } = string.Empty;
        public int ExpiresIn { get; set; }
        public string? RefreshToken { get; set; }
        public string? Scope { get; set; }

        // 为了与现有代码兼容
        public string Token => AccessToken;
    }
}